﻿
namespace MVVMAppINotifyDataErrorInfo.ViewModel
{
    using System;
    using GalaSoft.MvvmLight;
    using System.ComponentModel;
    using System.Collections;
    using System.Collections.Generic;
    using System.Threading;
    using System.Linq;
    using System.Collections.ObjectModel;

    public abstract class ViewModelNotifyDataError
        : ViewModelBase, INotifyDataErrorInfo
    {
        ObservableCollection<ErrorContent> _errors;
        public ObservableCollection<ErrorContent> Errors
        {
            get 
            {
                if (_errors == null)
                {
                    _errors = new ObservableCollection<ErrorContent>();
                }
                return _errors;
            }
        }            

        protected Dictionary<string, List<string>> PropertyDependencies =
            new Dictionary<string, List<string>>();

        #region INotifyDataErrorInfo implementation

        public event EventHandler<DataErrorsChangedEventArgs> 
                ErrorsChanged = delegate { };

        public virtual IEnumerable GetErrors(string propertyName)
        {   
            IEnumerable<string> ret = null;
            
            ret = Errors
                    .Where<ErrorContent>(e => e.PropertyName == propertyName)
                    .Select<ErrorContent, string>(e => e.ErrorMessage);


            return ret;
        }

        public bool HasErrors
        {
            get { return false; }
        }

        #endregion

        #region Protected Methods

        protected void ValidateProperty(string propertyName)
        {
            this.VerifyPropertyName(propertyName);
      
            var worker = new BackgroundWorker();            
           
            worker.DoWork += 
                (o, e) =>
                    {
                        Thread.Sleep(4000); //simulated validation delay
                        e.Result = ValidatePropertySpecialized(propertyName);
                    };

            worker.RunWorkerCompleted +=
                (o, e) =>
                {
                    IEnumerable<string> messages =
                        e.Error == null ?
                            (IEnumerable<string>)e.Result :
                            Enumerable.Repeat<string>(e.Error.Message, 1);

                    UpdateErrors(propertyName, messages);
                    
                };

            worker.RunWorkerAsync();
        }

        private void RaiseErrorsChanged(string propertyName)
        {   
            ErrorsChanged(this, new DataErrorsChangedEventArgs(propertyName));

            if (PropertyDependencies.ContainsKey(propertyName))
            {
                foreach (string item in PropertyDependencies[propertyName])
                {
                    //ErrorsChanged(this, new DataErrorsChangedEventArgs(item));
                    ValidateProperty(item);
                }
            }
        }

        protected abstract IEnumerable<string> ValidatePropertySpecialized(string propertyName);
       
        protected virtual void UpdateErrors(string propertyName, IEnumerable<string> errors)
        {
            Errors
                .Where<ErrorContent>(e => e.PropertyName == propertyName)
                .ToList<ErrorContent>()
                .ForEach((element) =>
                    {
                        Errors.Remove(element);
                    } );

            if (errors != null)
            {
                foreach (string item in errors)
                {
                    Errors.Add(new ErrorContent() { PropertyName = propertyName, ErrorMessage = item });                    
                }
            }
            
            RaiseErrorsChanged(propertyName);
        }

        #endregion
    }
}
